﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Top.PointStore.Business;
using Top.PointStore.LuckTurntable.Model;
using Top.Data;
using Top.Web;
using Top.Redis;
using Top.Web.Api;
using Top.Core;
using StackExchange.Redis;

namespace Top.PointStore.LuckTurntable
{
    public static class LuckBigWheel
    {

        //奖品序列
        private static ConcurrentDictionary<int, LuckQueue> luckyQueue = new ConcurrentDictionary<int, LuckQueue>();

        //剩余奖品数
        private static int residueProductCount = 0;

        //实时监控
        public static List<LuckQueue> luckyQueuelook = new List<LuckQueue>();

        //抽奖配置
        private static List<LuckProduct> rule = new List<LuckProduct>();

        //控制当前用户并发Turn请求
        private static ConcurrentDictionary<Guid, int> IsUserBuying = new ConcurrentDictionary<Guid, int>(4 * Environment.ProcessorCount, 800);

        //自旋锁控制并发数据操作
        private static System.Threading.SpinLock spinLock = new System.Threading.SpinLock();

        //内存中奖纪录
        private static ConcurrentDictionary<T_LuckRecordExpand, int> memoryLuckrecord = new ConcurrentDictionary<T_LuckRecordExpand, int>();

        //中奖纪录压缩值
        private static int memoryLuckrecordCompress = 0;

        //已抽奖次数
        private static ConcurrentDictionary<Guid, int> userLuckNumberOfTimes = new ConcurrentDictionary<Guid, int>();

        //未中奖配置
        private static LuckProduct notWin = new LuckProduct()
        {
            Angle = 0,
            Chance = 0,
            Count = 0,
            Description = "谢谢参与",
            Kid = 0,
            LuckCount = 0,
            MaxCount = 0,
            Name = "谢谢参与",
            Point = 0,
            Rate = 1
        };

        private static Random r = new Random();

        public static void Init()
        {
            InitRule().Wait();            
            InitUserLuckNumberOfTimes().Wait();
            InitLuckQueue();
        }

        public static void InitLuckQueue()
        {
            residueProductCount = 0;
            luckyQueue.Clear();
            luckyQueuelook.Clear();
            int index = 0;
            foreach (LuckProduct item in rule)
            {
                residueProductCount += item.Count;
                LuckQueue lq = new LuckQueue()
                {
                    Num = item.Count,
                    Product = item
                };
                luckyQueuelook.Add(lq);
                for (int i = 0; i < item.Chance * 100; i++)
                {
                    luckyQueue.TryAdd(index, lq);
                    index++;
                }
            }
            int residue = 10000 - luckyQueue.Count();
            if (residue > 0)
            {
                LuckQueue nullQueue = new LuckQueue()
                {
                    Num = 0,
                    Product = null
                };
                for (int i = 0; i < residue; i++)
                {
                    luckyQueue.TryAdd(index, nullQueue);
                    index++;
                }
            }
        }

        public async static Task<int> InitRule()
        {
            List<LuckProduct> productList = new List<LuckProduct>();
            string sql = @"select * from t_luckproduct";
            using (var db = DataBaseContext.GetDataBase("reader"))
            {
                productList = await db.Sql(sql).ToList<LuckProduct>();
            }
            rule = productList;
            foreach (LuckProduct item in rule)
            {
                if (item.Kid==-1)
                {
                    notWin = item;
                }
            }
            return 1;
        }

        public async static Task<int> InitUserLuckNumberOfTimes()
        {
            string sql = @"select DISTINCT F_UserToken from v_luckrecord WHERE CreateTime > {0}";
            using (var db = DataBaseContext.GetDataBase("reader"))
            {
                var list =await db.Sql(sql, "2016-2-7").ToList<Guid>();
                foreach (var item in list)
                {
                    userLuckNumberOfTimes.TryAdd(item, 1);
                }

            }
            return 1;
        }

        public static async Task<LuckyResult> Turn(OAuthUser user, T_PointMain_new pointMain)
        {
            int turnTime = Convert.ToInt32(Configs.AppSettings.Get("BigTurnTableTurnTime"));
            int turnPoint = Convert.ToInt32(Configs.AppSettings.Get("BigTurnTableTurnPoint"));
            var res = new LuckyResult();
            res.IsUsePoint = true;
            res.State = 1;
            res.isWin = false;
            res.LuckProduct = notWin;
            string description = ActivityCheck();
            if (description!="ok")
            {
                res.State = -1;
                res.Message = description;
                return res;
            }

            if (pointMain.F_PointBalance < turnPoint)
            {
                res.Message = "您的积分不足!";
                res.State = -1;
                res.isWin = false;
                res.LuckProduct = notWin;
                return res; 
            }
                      
            if (!IsUserBuying.TryAdd(user.UserToken, 1))
            {
                res.Message = "抽奖人数过多排队中,请稍后再试!";
                res.State = -4;
                res.isWin = false;
                res.LuckProduct = notWin;
                return res;
            }
            
            try
            {
                int lucknumbercount = userLuckNumberOfTimes.GetOrAdd(user.UserToken, 0);
                if (lucknumbercount >= turnTime)
                {
                    res.Message = "您的抽奖机会已用完!";
                    res.State = -1;
                    res.isWin = false;
                    res.LuckProduct = notWin;
                    return res;
                }
                int newlucknumbercount = lucknumbercount + 1;
                userLuckNumberOfTimes.TryUpdate(user.UserToken, newlucknumbercount, lucknumbercount);

                pointMain.F_PointCash = pointMain.F_PointCash + turnPoint;
                pointMain.F_PointBalance = pointMain.F_PointBalance - turnPoint;

                int random = 0;
                LuckQueue lq = new LuckQueue();
                int cnt = 0;
                while (true)
                {
                    cnt++;
                    random = r.Next(10000);
                    lq = luckyQueue[random];
                    if (lq.Product.Kid==-1)
                    {
                        break;
                    }
                    if (lq.Num > 0)
                    {
                        break;
                    }
                    if (cnt>=20)
                    {
                        break;
                    }
                }                                  
                if (lq.Num < 1)
                {
                    return res;
                }
                else
                {
                    bool getLock = false;
                    try
                    {
                        spinLock.Enter(ref getLock);
                        if (lq.Num > 0)
                        {
                            lq.Num = lq.Num - 1;
                            lq.Out = lq.Out + 1;
                            residueProductCount =residueProductCount- 1;
                        }
                    }
                    finally
                    {
                        if (getLock) spinLock.Exit();
                    }
                }
                if (lq.Num < 0)
                {
                    return res;
                }
                var exchangeKid = 0;
                string companyName = string.Empty;
                if (MemoryTable.TB_Broker.ContainsKey(user.BrokerID))
                {
                    companyName = MemoryTable.TB_Broker[user.BrokerID].F_BrokerCompanyName;
                }
                
                //构造数据
                T_LuckRecordExpand luckRecord = new T_LuckRecordExpand();
                luckRecord.UserToken = user.UserToken.ToString();
                luckRecord.ExchangeKid = exchangeKid;
                luckRecord.LuckProductKid = lq.Product.Kid;
                luckRecord.OrderNo = DateTime.Now.ToString("yyyyMMddHHmmfff");
                luckRecord.UserName = (user.Name ?? user.NickName)??"";
                luckRecord.PhoneNumber = user.Phone??"";
                luckRecord.F_CompanyName = companyName;// user.F_BrokerCompanyName;
                luckRecord.F_IsDisplay = lq.Product.Point > 0 ? 0 : 1;
                luckRecord.IsWin = 1;
                luckRecord.CreateTime = DateTime.Now;
                luckRecord.F_ProductName = lq.Product.Name;
                luckRecord.BrokerKid = user.BrokerID;

                //抽中奖品，扣积分
                if (res.LuckProduct.Count > 0)
                {
                    //幸运大转盘抽奖消耗 gc*
                    int resultID = LuckProductManager.UpdateBrokerPoint(pointMain, user, turnPoint, "broker.pointluck.cash");
                    if (resultID <= 0)
                    {
                        res.Message = "谢谢参与";//感谢参与
                        res.isWin = false;
                        notWin.Description = "谢谢参与";
                        res.LuckProduct = notWin;
                        return res;
                    }                    
                }
                
                exchangeKid = await AddLuckyRecord(luckRecord, user);//返回中奖纪录ID
                if (exchangeKid <= 0)
                {
                    return res;
                }

                try
                {                    
                    long resultPubID = await RedisHelper.Publish("TopsHandleLuckRecordWindowsService", string.Format("{0}#{1}#{2}#{3}", exchangeKid.ToString(), user.UserToken,turnPoint,user.BrokerID));
                    if (resultPubID <= 0)
                    {
                        return res;
                    }
                }
                catch (Exception)
                {
                    int temp = 0;
                    memoryLuckrecord.TryRemove(luckRecord, out temp);
                    res.Message = "谢谢参与";//感谢参与
                    res.isWin = false;
                    notWin.Description = "谢谢参与";
                    res.LuckProduct = notWin;
                    return res;
                }
                res.isWin = true;
                res.LuckProduct = lq.Product;
                if (lq.Product.Point>0)
                {
                    pointMain.F_PointAll = pointMain.F_PointAll +Convert.ToInt32(lq.Product.Point);
                    pointMain.F_PointBalance = pointMain.F_PointBalance + Convert.ToInt32(lq.Product.Point);
                }
                return res;
            }
            catch
            {
                return res;
            }
            finally
            {
                int value = 0;
                if (!IsUserBuying.TryRemove(user.UserToken, out value))
                {
                    IsUserBuying.TryRemove(user.UserToken, out value);
                }
            }
        }

        public static async Task<LuckyResult> Turn_RedPacket(OAuthUser user, T_PointMain_new pointMain)
        {
            int turnTime = Convert.ToInt32(Configs.AppSettings.Get("BigTurnTableTurnTime"));
            int turnPoint = Convert.ToInt32(Configs.AppSettings.Get("BigTurnTableTurnPoint"));
            var res = new LuckyResult();
            res.IsUsePoint = false;
            res.State = 1;
            res.isWin = false;
            res.LuckProduct = notWin;
            string description = ActivityCheck();
            if (description != "ok")
            {
                res.State = -1;
                res.Message = description;
                return res;
            }
          
            if (!IsUserBuying.TryAdd(user.UserToken, 1))
            {
                res.Message = "别着急一个一个来!";
                res.State = -4;
                res.isWin = false;
                res.LuckProduct = notWin;
                return res;
            }

            try
            {
                int lucknumbercount = userLuckNumberOfTimes.GetOrAdd(user.UserToken, 0);
                if (lucknumbercount >= turnTime)
                {
                    res.Message = "您已抢过红包了!";
                    res.State = -1;
                    res.isWin = false;
                    res.LuckProduct = notWin;
                    return res;
                }
               
                int newlucknumbercount = lucknumbercount + 1;
                userLuckNumberOfTimes.TryUpdate(user.UserToken, newlucknumbercount, lucknumbercount);

                if (pointMain == null)
                {
                    res.State = 1;
                    res.isWin = false;
                    res.LuckProduct = notWin;
                    res.Message = "猴旺雷，一个擦肩而过的红包给你换来一整年的好运气，加油！";
                    return res;
                }

                if (user.BrokerID == 0)
                {
                    res.State = 1;
                    res.isWin = false;
                    res.LuckProduct = notWin;
                    res.Message = "猴旺雷，一个擦肩而过的红包给你换来一整年的好运气，加油！";
                    return res;
                }

                int random = 0;
                LuckQueue lq = new LuckQueue();
                int cnt = 0;
                while (true)
                {
                    cnt++;
                    random = r.Next(10000);
                    lq = luckyQueue[random];
                    if (lq.Product.Kid == -1)
                    {
                        break;
                    }
                    if (lq.Num > 0)
                    {
                        break;
                    }
                    if (cnt >= 20)
                    {
                        break;
                    }
                }
                if (lq.Num < 1)
                {
                    return res;
                }
                else
                {
                    bool getLock = false;
                    try
                    {
                        spinLock.Enter(ref getLock);
                        if (lq.Num > 0)
                        {
                            lq.Num = lq.Num - 1;
                            lq.Out = lq.Out + 1;
                            residueProductCount = residueProductCount - 1;
                        }
                    }
                    finally
                    {
                        if (getLock) spinLock.Exit();
                    }
                }
                if (lq.Num < 0)
                {
                    return res;
                }
                var exchangeKid = 0;
                string companyName = string.Empty;
                if (MemoryTable.TB_Broker.ContainsKey(user.BrokerID))
                {
                    companyName = MemoryTable.TB_Broker[user.BrokerID].F_BrokerCompanyName;
                }

                //构造数据
                T_LuckRecordExpand luckRecord = new T_LuckRecordExpand();
                luckRecord.UserToken = user.UserToken.ToString();
                luckRecord.ExchangeKid = exchangeKid;
                luckRecord.LuckProductKid = lq.Product.Kid;
                luckRecord.OrderNo = "";
                luckRecord.UserName = (user.Name ?? user.NickName) ?? "";
                luckRecord.PhoneNumber = user.Phone ?? "";
                luckRecord.F_CompanyName = companyName;// user.F_BrokerCompanyName;
                luckRecord.F_IsDisplay = 1;
                luckRecord.IsWin = 1;
                luckRecord.CreateTime = DateTime.Now;
                luckRecord.F_ProductName = lq.Product.Name;
                luckRecord.BrokerKid = user.BrokerID;

                exchangeKid = await AddLuckyRecord(luckRecord, user);//返回中奖纪录ID
                if (exchangeKid <= 0)
                {
                    return res;
                }

                try
                {
                    long resultPubID = await RedisHelper.Publish("TopsHandleLuckRecordWindowsService", string.Format("{0}#{1}#{2}#{3}", exchangeKid.ToString(), user.UserToken, turnPoint, user.BrokerID));
                    if (resultPubID <= 0)
                    {
                        //return res;
                        int temp = 0;
                        memoryLuckrecord.TryRemove(luckRecord, out temp);
                        res.Message = "猴旺雷，一个擦肩而过的红包给你换来一整年的好运气，加油！";//感谢参与
                        res.isWin = false;
                        notWin.Description = "猴旺雷，一个擦肩而过的红包给你换来一整年的好运气，加油！";
                        res.LuckProduct = notWin;
                        return res;
                    }
                }
                catch (Exception)
                {
                    int temp = 0;
                    memoryLuckrecord.TryRemove(luckRecord, out temp);
                    res.Message = "猴旺雷，一个擦肩而过的红包给你换来一整年的好运气，加油！";//感谢参与
                    res.isWin = false;
                    notWin.Description = "猴旺雷，一个擦肩而过的红包给你换来一整年的好运气，加油！";
                    res.LuckProduct = notWin;
                    return res;
                }
                res.isWin = true;
                res.LuckProduct = lq.Product;                
                return res;
            }
            catch
            {
                return res;
            }
            finally
            {
                int value = 0;
                if (!IsUserBuying.TryRemove(user.UserToken, out value))
                {
                    IsUserBuying.TryRemove(user.UserToken, out value);
                }
            }
        }

        public static async Task<int> AddLuckyRecord(T_LuckRecordExpand model, OAuthUser user)
        {

            bool getRecordLock = false;
            try
            {
                spinLock.Enter(ref getRecordLock);
                memoryLuckrecord.TryAdd(model, 1);
            }
            finally
            {
                if (getRecordLock) spinLock.Exit();
            }

            string tokenStr = user.UserToken.ToString();
            char markchar = Convert.ToChar(tokenStr.Substring(tokenStr.Length - 2, 1));
            int mark = (int)markchar;
            string tableName = string.Empty;
            switch (mark % 4)
            {
                case 0:
                    tableName = "T_LuckRecord";
                    break;
                case 1:
                    tableName = "T_LuckRecord2";
                    break;
                case 2:
                    tableName = "T_LuckRecord3";
                    break;
                case 3:
                    tableName = "T_LuckRecord4";
                    break;
                default:
                    tableName = "T_LuckRecord";
                    break;
            }

            string sql = @"INSERT INTO " + tableName + @"
            		                    (UserName
            		                    ,PhoneNumber
            		                    ,OrderNo
            		                    ,BrokerKid
            		                    ,ExchangeKid
            		                    ,LuckProductKid
            		                    ,F_IsDisplay
            		                    ,F_CompanyName
            		                    ,CreateTime
            		                    ,IsWin
                                        ,F_UserToken)
            	                    VALUES
            		                    ({0:UserName}
            		                    ,{0:PhoneNumber}
            		                    ,{0:OrderNo}
            		                    ,{0:BrokerKid}
            		                    ,{0:ExchangeKid}
            		                    ,{0:LuckProductKid}
            		                    ,{0:F_IsDisplay}
            		                    ,{0:F_CompanyName}
            		                    ,{0:CreateTime}
            		                    ,{0:IsWin}
                                        ,{0:UserToken});
                            select LAST_INSERT_ID();";
            using (var db = new DbTour())
            {
                return await db.Sql(sql, model).ExecuteScalar<int>(0);
            }
        }

        public static List<T_LuckRecordExpand> GetLuckRecordList()
        {
            if ((memoryLuckrecord.Keys.Count / 20) > (memoryLuckrecordCompress/10))
            {
                bool getLock = false;
                try
                {
                    spinLock.Enter(ref getLock);
                    if ((memoryLuckrecord.Keys.Count / 20) > (memoryLuckrecordCompress/10))
                    {
                        memoryLuckrecordCompress += 10;  
                    }                                      
                }
                finally
                {
                    if (getLock) spinLock.Exit();
                }                                
            }
            return memoryLuckrecord.Keys.Skip(memoryLuckrecordCompress).OrderByDescending(a => a.CreateTime).Take(20).ToList();
        }

        public static int GetLuckRecordCount()
        {
            int recoredCount = memoryLuckrecord.Keys.Count;            
            return recoredCount;
        } 

        public static List<LuckProduct> GetLuckProductList()
        {
            return rule;
        }

        public static async Task<bool> CheckLuckyRecord(string brokerUserToken, string OutTransactionNumber, double amount)
        {
            if (!OutTransactionNumber.Contains('|'))
            {
                return false;
            }
            string[] temp = OutTransactionNumber.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
            if (string.IsNullOrWhiteSpace(brokerUserToken)) return false;
            string sql = @"
                select a.Kid as LuckyId,IFNULL(b.Point,0) AS Money from V_LuckRecord a left join t_luckproduct b on a.LuckProductKid = b.Kid
                where 1=1
                and a.F_UserToken = {0}
                and a.kid = {1}
                and b.point = {2}
                and a.CreateTime>'2016-1-29'
                order by a.CreateTime desc
                limit 1
                ";
            using (var db = DataBaseContext.GetDataBase("reader"))
            {
                var luckmodel = await db.Sql(sql, brokerUserToken, Convert.ToInt32(temp[1]), amount).FirstOrDefault<LuckInfo>();
                if (luckmodel != null)
                {
                    return true;
                }
                return false;
            }
//            string sql = @"
//                select a.Kid as LuckyId,IFNULL(b.Point,0) AS Money from V_LuckRecord a left join t_luckproduct b on a.LuckProductKid = b.Kid
//                where 1=1
//                and a.F_UserToken = {0}
//                and a.CreateTime>'2016-1-31'
//                limit 1
//                ";
//            using (var db = DataBaseContext.GetDataBase("reader"))
//            {
//                var luckmodel = await db.Sql(sql, brokerUserToken).FirstOrDefault<LuckInfo>();
//                if (luckmodel != null)
//                {
//                    if (luckmodel.LuckyId==Convert.ToInt32(temp[1])&&luckmodel.Money==amount)
//                    {
//                        return true;
//                    }
//                }
//                return false;
//            }
        }

        public static string ActivityCheck()
        {
            dynamic obj = LuckActivityManager.ActivityGet();
            if (obj.isOpen=="0")
            {
                return "暂时没有活动";
            }
            DateTime btime = Convert.ToDateTime(obj.beginTime + " " + obj.exchangeBeginTime);
            DateTime etime = Convert.ToDateTime(obj.endTime + " " + obj.exchangeEndTime);
            if (DateTime.Now<btime)
            {
                return "活动尚未开始,请耐心等待";
            }
            if (DateTime.Now >etime)
            {
                return "活动已结束";
            }
            DateTime todayBtime = Convert.ToDateTime((DateTime.Now.Date.ToString("yyyy-MM-dd")+" "+obj.exchangeBeginTime));
            DateTime todayEtime = Convert.ToDateTime((DateTime.Now.Date.ToString("yyyy-MM-dd")+" "+obj.exchangeEndTime));
            if (DateTime.Now < todayBtime)
            {
                TimeSpan ts = todayBtime-DateTime.Now;
                string residue = ts.Hours+":"+ts.Minutes+":"+ts.Seconds;
                return "活动即将开始，剩余时间：" + residue;
            }
            if (residueProductCount<1)
            {
                return "红包已经抢光了，祝您新年快乐";
            }
            if (DateTime.Now>todayEtime)
            {
                return "活动已结束";
            }
            return "ok";
        }
         
    }
}
